#include "biblioteca.h"

void mutacao_local_troca(DP_INDI pop){
    void mutacao_local(P_INDI indi){
        int local1 = random() % SIZE_MATERIAL;
        int local2 = random() % SIZE_MATERIAL;
        while( local1 == local2 )
            local2 = random() % SIZE_MATERIAL;
        int i = indi->sendmory[local1];
        indi->sendmory[local1] = indi->sendmory[local2];
        indi->sendmory[local2] = i;
    }
    int max = ((TCROSS*SIZE_POP)/100);
    register int i = 0;
    for ( ; i < max; ++i){
        if ( random() % 100 < TMUT ){
            //int local_mut = random() % max;
            mutacao_local(pop[SIZE_POP + i]);
        }
    }
}

int *acha_ciclo(P_INDI indi1, P_INDI indi2){
    //encontra em que posicao esta o numero @number no vetor @material, cujo tamanho é @size
    int acha_pos(int *material, int size, int number){
        register int i = 0;
        for ( ; i < size; ++i)
            if ( material[i] == number )
                return i;
        return 0;
    }
    //sorteia uma posicao aleatoria, e cria o vetor máscara
    int pos = random()%SIZE_MATERIAL;
    //cria o vetor máscara e o preenche com zeros
    int *booleanos = calloc(1, SIZE_MATERIAL*sizeof(int));
    //enquanto não encontrar uma posicao no vetor pos que já está preenchida
    while( (booleanos[pos]) == 0 ){
        //preenche a posicao atual com 1 e encontra a posicao no vetor 2 que contém o numero apontado na posicao atual
        booleanos[pos] = 1;
        pos = acha_pos(indi2->sendmory, SIZE_MATERIAL, indi1->sendmory[pos]);
    }
    return booleanos;
}

//retorna um vetor de 2 * tamanho do material, com dois materiais pra dois filhos
int *crossover_ciclico(P_INDI indi1, P_INDI indi2){
    int *mascara = acha_ciclo(indi1, indi2);
    //tinha um problema de alocação de memória
    int *retorno = calloc(2, SIZE_MATERIAL*sizeof(int*));
    //filho 1 segue a mascara do pai1, filho 2 segue a mascara do pai2
    register int i = 0;
    for( ; i < SIZE_MATERIAL; ++i){
        //se for bit 1 significa que o filho 1 deve pegar material do pai2 e filho 2 do pai 1
        if ( mascara[i] ){
            retorno[i] = indi2->sendmory[i];
            retorno[i+SIZE_MATERIAL] = indi1->sendmory[i];
        //caso contrario (default), filho 1 pega material do pai 1 e filho 2 pega material do pai 2
        }else{
            retorno[i] = indi1->sendmory[i];
            retorno[i+SIZE_MATERIAL] = indi2->sendmory[i];
        }
    }
    return retorno;
}

    /*  
     *  Precisa receber uma população @pop e um ponteiro @pais do tipo DP_INDI com duas posições
     **/
void torneio(DP_INDI pop, DP_INDI pais){
    void umPaiTour(DP_INDI pop, DP_INDI pai){
        DP_INDI temp[TOUR];
        register int i = 0;
        int *usados = calloc(SIZE_POP, sizeof(int));
        for( ; i < TOUR; ++i){
            int t = random()%SIZE_POP;
            while( usados[t] ) t = random()%SIZE_POP;
            usados[t] = 1;
            memcpy(temp+i, pop+t, sizeof(P_INDI));
        }
        qsort(temp, TOUR, sizeof(P_INDI), compara_avaliacao_individuos_const_void);
        memcpy(pai, temp, sizeof(P_INDI));
        free(usados);
    }
    umPaiTour(pop, pais);
    umPaiTour(pop, pais+1);
}

void executa_algoritmo_genetico(DP_INDI populacao, void(*seleciona_pais)(DP_INDI pop, DP_INDI pais), int *(*crossover)(P_INDI indi1, P_INDI indi2),
                                int (*compara_individuos)(const void* indi1, const void* indi2), void (*avaliador)(P_INDI indi), void (*mutacao)(DP_INDI indi)){

    register int execucao_atual = 0, geracao_atual, i, solucao_found;
    int tamanho_extra = ((TCROSS*SIZE_POP)/100), vezes_encontrada = 0;
    int tamanho_total = SIZE_POP + tamanho_extra;

    for( ; execucao_atual < EXECUCOES; ++execucao_atual){
        printf("\tExecução atual: %d\n",execucao_atual + 1);
        solucao_found = 0;
        DP_INDI pop = calloc(tamanho_total, sizeof(P_INDI));
        //alocou nova populacao copiando os pais
        for( i = 0; i < SIZE_POP; ++i){
            pop[i] = calloc(1, sizeof(INDI));
            if ( i < SIZE_POP ){
                memcpy(pop+i, populacao+i, sizeof(INDI));
            }
        }
        //até aqui, a populacao tem tamanho SIZE_POP!
        qsort(pop, SIZE_POP, sizeof(P_INDI), compara_individuos);
        for( geracao_atual = 0; geracao_atual < NUM_GERACOES; ++geracao_atual){
            for( i = 0; i < SIZE_POP; ++i){
                avaliador(pop[i]);
                if ( pop[i]->evaluate == 0 ){
                    printf("Solução encontrada!\n");
                    qsort(pop, tamanho_total, sizeof(P_INDI), compara_individuos);
                    solucao_found = 1;
                    ++vezes_encontrada;
                    break;
                }
            }
            if ( solucao_found == 1 )
                break;
            //gerar filhos
            for( i = 0; i < tamanho_extra; i += 2){
                //seleciona dois pais
                P_INDI pais[2];
                seleciona_pais(pop, pais);
                //cria dois filhos
                int *filhos = crossover(pais[0], pais[1]);
                INDI i1 = create_individuo_com_material(filhos);
                INDI i2 = create_individuo_com_material(filhos+SIZE_MATERIAL);
                //copia eles para a populacao
                pop[SIZE_POP + i] = copiar_individuo(&i1);
                pop[SIZE_POP + i + 1] = copiar_individuo(&i2);
            }
            mutacao(pop);
            for( i = 0; i < tamanho_total; ++i)
                avaliador(pop[i]);
            #ifdef ELITISMO
                //ordena a população de pais e preserva a elite, copiar os filhos sobre o restante dos pais
                qsort(pop, SIZE_POP, sizeof(P_INDI), compara_individuos);
                copy_population_to_other(pop+SIZE_POP, pop, tamanho_extra);
            #else
                //ordena a população
                qsort(pop, tamanho_total, sizeof(P_INDI), compara_individuos);
                //copy_population_to_other(pop_nova, pop, calculo);
            #endif
        }
        printf("Melhor indivíduo da execução:\n\t");
        imprime_individuo(pop[0]);
        free(pop);
    }
    printf("Solucao encontrada %d vezes em %d execucoes. (Convergencia de %.5f)\n",vezes_encontrada, EXECUCOES, (float)vezes_encontrada/EXECUCOES);
    printf("EXPERIMENTO: %3d\tSIZEPOP: %3d\tPCROSS: %3d\tPMUT: %3d\t TOUR: %3d\t NRO GERACOES: %3d\n",EXPERIMENTO, SIZE_POP, TCROSS, TMUT, TOUR, NUM_GERACOES);
}

